#include "Classes.h";
#include <iostream>
#include <math.h>


BigInteger::Number:: Number (const std::string&  i_str) throw (const std::string&)
{
	if(i_str.size()==0)
		throw std::string("unable to initialise BigIntegr with an empty string");
	m_if_negative=false;
	int i=0;
	if(i_str[0]=='-')
	{
		m_if_negative=true;
		++i;
	}
	if(i==1)
		m_vec_number.resize(i_str.size()-1);
	else
		m_vec_number.resize(i_str.size());
	for(int j =0;j<m_vec_number.size();j++)	
		m_vec_number[j]=i_str[i++];
	int nul_am=0;
	while(m_vec_number[nul_am]==0)
	{
		nul_am++;
		if(nul_am==m_vec_number.size())
			break;
	}
	if(nul_am!=m_vec_number.size())
		m_vec_number.erase(m_vec_number.begin(),m_vec_number.begin()+nul_am);
	else
	{	
		if(nul_am!=0)	
			m_vec_number.erase(m_vec_number.begin(),m_vec_number.begin()+nul_am-1);
	}
};


std::string BigInteger::Number:: GetStr() const
{
	std::string s;
	if(m_vec_number.size()==0)
	{
		s.push_back(48);
		return s;
	}
	if(m_if_negative)
		s.push_back('-');
	for(int i=0;i<m_vec_number.size();i++)
		s.push_back(m_vec_number[i]+48);
	return s;
};

BigInteger::Number& BigInteger:: Number:: operator = (const Number& i_other)
{
	if(this==&i_other)
		return *this;
	this->m_vec_number=i_other.m_vec_number;
	this->m_if_negative=i_other.m_if_negative;
	return *this;
};

BigInteger::Number& BigInteger:: Number:: operator = (const std::string& i_str) throw (const std::string&) 
{
	if(i_str.size()==0)
		throw std::string("unable to initialise BigIntegr with an empty string");
	std::vector<Digit> help(i_str.size());
	bool if_negative = false;
	int i=0;
	if(i_str[0]=='-')
	{
		help.resize(i_str.size()-1);
		if_negative=true;
		++i;
	}	
	for(int j =0;j<help.size();j++)
		help[j]=i_str[i++];
	int nul_am=0;
	while(help[nul_am]==0)
	{
		nul_am++;
		if(nul_am==help.size())
			break;
	}
	if(nul_am==help.size())
		help.erase(help.begin(),help.begin()+nul_am-1);
	else
	{	
		if(nul_am!=0)
			help.erase(help.begin(),help.begin()+nul_am);
	}
	this->m_vec_number=help;
	this->m_if_negative=if_negative;
	return *this;
};

BigInteger::Number& BigInteger:: Number:: operator = (std::list<Digit>& i_other)
{
	m_vec_number.resize(i_other.size());
	for(int i=0; i<m_vec_number.size(); i++)
	{
		m_vec_number[i]=i_other.front();
		i_other.pop_front();
	}
	return *this;
};

bool BigInteger::Number:: operator == (const Number& i_other) const
{
	if(this->m_if_negative!=i_other.m_if_negative)
		return false;
	if(this->m_vec_number.size()!=i_other.m_vec_number.size())
		return false;
    if(m_vec_number!=i_other.m_vec_number)
		return false;
	return true;
};

bool BigInteger::Number:: operator < (const Number& i_other) const
{
	if(m_if_negative!=i_other.m_if_negative)
	{
		if(m_if_negative==true)
			return true;
		else
			return false;
	}
	if(m_vec_number.size()!=i_other.m_vec_number.size())
	{
		if(m_vec_number.size()<i_other.m_vec_number.size())
		{
			if(m_if_negative==true)
				return false;
			else
				return true;
		}
		else
			{
				if(m_if_negative==true)
					return true;
				else
					return false;
			}
	}
	if(m_vec_number<i_other.m_vec_number)
	{
		if(m_if_negative==false)
			return true;
		else
			return false;
	}
	if(m_vec_number>i_other.m_vec_number)
	{
		if(m_if_negative==false)
			return false;
		else
			return true;
	}
	return false;
};

bool BigInteger::Number:: operator != (const Number& i_other) const
{
	return(!this->operator== (i_other));
};

bool BigInteger::Number:: operator > (const Number& i_other) const
{
	return(i_other.operator< (*this));
};

BigInteger::Number  BigInteger::Number:: operator - () const
{
	Number temp=*this;
	temp.m_if_negative=!temp.m_if_negative;
	return temp;
};

BigInteger::Number BigInteger::Number:: operator + (const Number& i_other)
{
	Number temp;
	if(this->m_if_negative == i_other.m_if_negative)
	{
		if(m_if_negative==false)
			temp.m_if_negative=false;
		else
			temp.m_if_negative=true;
		temp.m_vec_number=p_PlusVector(i_other.m_vec_number);
	}
	else
	{
		if(m_if_negative==false)
		{
			if(this->operator>(-i_other))
			{
				temp.m_if_negative=false;
				temp.m_vec_number=p_MinusVector(i_other.m_vec_number);
			}
			if(this->operator<(-i_other))
			{
				temp.m_if_negative=true;
				temp.m_vec_number=i_other.p_MinusVector(this->m_vec_number);
			}
		}
		else
		{
			if(this->operator>(-i_other))
			{
				temp.m_if_negative=false;
				temp.m_vec_number=i_other.p_MinusVector(this->m_vec_number);
			}
			if(this->operator<(-i_other))
			{
				temp.m_if_negative=true;
				temp.m_vec_number=p_MinusVector(i_other.m_vec_number);
			}
		}
	}
	return temp;
};

BigInteger::Number BigInteger::Number:: operator - (const Number& i_other)
{
	Number temp;
	if(this->m_if_negative == i_other.m_if_negative)
	{
		if(m_if_negative==false)
		{
			if(this->operator>(i_other))
			{
				temp.m_if_negative=false;
				temp.m_vec_number=p_MinusVector(i_other.m_vec_number);
			}
			if(this->operator<(i_other))
			{
				temp.m_if_negative=true;
				temp.m_vec_number=i_other.p_MinusVector(this->m_vec_number);
			}
		}
		else
		{
			if(this->operator>(i_other))
			{
				temp.m_if_negative=false;
				temp.m_vec_number=i_other.p_MinusVector(this->m_vec_number);
			}
			if(this->operator<(i_other))
			{
				temp.m_if_negative=true;
				temp.m_vec_number=p_MinusVector(i_other.m_vec_number);
			}
		}
	}
	else
	{
		if(m_if_negative==false)
			temp.m_if_negative=false;
		else
			temp.m_if_negative=true;
		temp.m_vec_number=p_PlusVector(i_other.m_vec_number);
	}
	return temp;
};

std::vector<Digit> BigInteger::Number:: p_PlusVector(const std::vector<Digit>& i_vec) const
{
	int this_size=m_vec_number.size();
	int other_size=i_vec.size();
	int max_size=std::max(this_size,other_size);
	std::vector<Digit> temp(max_size);
	max_size--;
	this_size--;
	other_size--;
	int k=0;
	int curr_value=0;
	while(this_size>=0 || other_size>=0)
	{
		if(this_size<0)
			curr_value=k+i_vec[other_size--] % 10;
		else
		{
			if(other_size<0)
				curr_value=k+m_vec_number[this_size--];
			else
				curr_value=k+m_vec_number[this_size--]+i_vec[other_size--] % 10;
		}
		temp[max_size--]=curr_value % 10;
		k=curr_value / 10;
	}
	if(k>0)
		temp.insert(temp.begin(),k);
	return temp;	
};


std::vector<Digit> BigInteger::Number:: p_MinusVector(const std::vector<Digit>& i_vec) const
{
	int this_size=m_vec_number.size();
	int other_size=i_vec.size();
	int j=this_size;
	std::vector<Digit> temp(this_size);
	j--;
	this_size--;
	other_size--;
	int k=0;
	int curr_value=0;
	while(this_size>=0)
	{	
		if(other_size<0)
		{
			if(m_vec_number[this_size]<k)
			{
				curr_value=10+m_vec_number[this_size--]-k;
				k=1;			
			}
			else
			{
				curr_value=m_vec_number[this_size--]-k;
				k=0;
			}
		}
		else
		{
			if(m_vec_number[this_size]<i_vec[other_size]+k)
			{
				curr_value=10+m_vec_number[this_size--]-i_vec[other_size--]-k;
				k=1;
			}
			else
			{
				curr_value=m_vec_number[this_size--]-i_vec[other_size--]-k;
				k=0;
			}
		}
		temp[j--]=curr_value;
	}
	int nul_am=0;
	while(temp[nul_am]==0)
		nul_am++;
	temp.erase(temp.begin(),temp.begin()+nul_am);
	return temp;	
};

BigInteger::Number BigInteger::Number:: operator * (const Number& i_other)
{
	Number sum="0";
	std::list<Digit> temp;
	Number to_add="0";
	int value;
	int k=0;
	
	for(int i=m_vec_number.size()-1; i>=0; i--)
	{
		for(int j=i_other.m_vec_number.size()-1; j>=0; j--)
		{
			value=m_vec_number[i]*i_other.m_vec_number[j];
			temp.push_front((value % 10+k) % 10);
			k=value / 10 + (value%10 + k) / 10;
		}
		if(k>0)
		{
			temp.push_front(k);
			k=0;
		}
		to_add=temp;	
		to_add.p_AddDigit(m_vec_number.size()-1-i,0);
		sum=sum+to_add;
	}
	if(m_if_negative==i_other.m_if_negative)
		sum.m_if_negative=false;
	else
		sum.m_if_negative=true;
	return sum;
};

void BigInteger::Number:: p_AddDigit(const int& amount,const int& i_digit)
{
	m_vec_number.insert(m_vec_number.end(),amount,i_digit);
};

BigInteger::Number BigInteger::Number:: operator / (const Number& i_other) throw (const std::string&)
{
	if(i_other.m_vec_number.size()==1 && i_other.m_vec_number[0]==0)
		throw std::string("division by zero");
	Number temp;
	Number copy_this,copy_other;
	if(this->m_if_negative==i_other.m_if_negative)
		temp.m_if_negative=false;
	else
		temp.m_if_negative=true;
	if(this->m_if_negative)
		copy_this=-*this;
	else
		copy_this=*this;
	if(i_other.m_if_negative)
		copy_other=-i_other;
	else
		copy_other=i_other;
	if (copy_this < copy_other)
	{
		temp="0";
		return temp;
	}
	if(copy_this == copy_other)
	{
		temp.m_vec_number.push_back(1);
		return temp;
	}
	Number help=copy_this.p_GetPiece(i_other.m_vec_number.size());
	if(help<copy_other)
		help.p_AddDigit(1,m_vec_number[help.m_vec_number.size()]);
	int pos=help.m_vec_number.size()-1;
	int pos_max=m_vec_number.size();
	Number divisor;
	Number diff="1";
	std::string divisor_str;
	while(pos<pos_max)
	{
		for(int i=1; i<10;i++)
		{
			divisor_str.push_back(i+1+48);
			if(i==9)
			{
				divisor_str.clear();
				divisor_str.push_back(9+48);
				temp.m_vec_number.push_back(divisor.m_vec_number[0]);
				divisor_str.clear();
				break;
			}
			divisor=divisor_str;
			if(divisor*copy_other>help)
			{
				divisor=divisor-diff;
				temp.m_vec_number.push_back(divisor.m_vec_number[0]);
				divisor_str.clear();
				break;
			}
			divisor_str.clear();
		}
		help=help-divisor*copy_other;
		int count_times=0;
		while(help<copy_other)
		{
			if(++pos>=pos_max)
				break;
			count_times++;
			help.p_AddDigit(1,m_vec_number[pos]);
			if(count_times>1)
				temp.m_vec_number.push_back(0);
		}
	}
	return temp;
};

BigInteger::Number BigInteger::Number:: p_GetPiece(const int& length)
{
	Number temp;
	temp.m_if_negative=false;
	temp.m_vec_number.resize(length);
	for(int i=0;i<length;i++)
		temp.m_vec_number[i]=m_vec_number[i];
	return temp;
};